﻿(*  
    F# Dataflow for ViewModels
    (c) Andras Janko, 2013, Available under Microsoft Public License.
*)

namespace FsFlow

open System

[<AutoOpen>]
module CompBuilders =
    let comp = CompBuilder Ticket.maxLevel
    let inline compL level = CompBuilder (byte level)
    let comp0 = CompBuilder 0uy
    let comp1 = CompBuilder 1uy
    let comp2 = CompBuilder 2uy
    let comp3 = CompBuilder 3uy

module Flow =
    /// Postpones refreshing computations until disposed
    let defer() = Core.defer()

    /// Creates a bindable variable with initial value v
    let inline var v = Variable(v) :> IVariable<_>
    /// Creates a bindable variable that uses an external storage   
    let inline prop getValue setValue = Prop(getValue, setValue) :> IVariable<_>

    let path varPath source =
        let res = new VariablePath<_,_>(source, varPath)
        res.Initialize()
        res :> IVariableP<_>
    let pathOpt varPath source =
        let res = new VariablePathOpt<_,_>(source, varPath)
        res.Initialize()
        res :> IVariableP<_>

    let map  mapping source = new Mapped<_,_>(source, mapping) :> IVariableP<_>
    let lmap mapping source = new LazyMapped<_,_>(source, mapping) :> IVariableP<_>

    let map2  mapping (source1, source2) = map  mapping <| VariableTuple<_,_>(source1, source2)
    let lmap2 mapping (source1, source2) = lmap mapping <| VariableTuple<_,_>(source1, source2)

    let map3  mapping (source1, source2, source3) = map  mapping <| VariableTuple<_,_,_>(source1, source2, source3)
    let lmap3 mapping (source1, source2, source3) = lmap mapping <| VariableTuple<_,_,_>(source1, source2, source3)

    let inline onChange action source = new MappedAction<_>(source, action) :> IDisposable

    let publish source =
        new Published<_>(source)
    let publishS setValue source =
        { new Published<_>(source) with override this.SetValue(v) = setValue v }
    let publishA setValue source =
        { new Published<_>(source) with override this.SetValue(v) = base.SetValue v; setValue v } 

    let publishOpt source =
        new PublishedOpt<_>(source) 
    let publishOptS setValue source =
        { new PublishedOpt<_>(source) with override this.SetValue(v) = setValue v } 
    let publishOptA setValue source =
        { new PublishedOpt<_>(source) with override this.SetValue(v) = base.SetValue v; setValue v } 

    let publishNull source = 
        new PublishedNull<_>(source) 
    let publishNullS setValue source =
        { new PublishedNull<_>(source) with override this.SetValue(v) = setValue v } 
    let publishNullA setValue source =
        { new PublishedNull<_>(source) with override this.SetValue(v) = base.SetValue v; setValue v } 

    let publishEnum source =
        new PublishedEnum<_>(source) 
    let publishEnumS setValue source =
        { new PublishedEnum<_>(source) with override this.SetValue(v) = setValue v } 
    let publishEnumA setValue source =
        { new PublishedEnum<_>(source) with override this.SetValue(v) = base.SetValue v; setValue v } 

    let constant v = new PublishedConst<_>(v) :> IVariableP<_>

    let command name source execute =
        new PublishedCommand(source, name, execute) :> ICommandP

    let commandE name execute = EnabledCommand(name, execute)

    let inline set v = VariableSet(v) :> IVariableSet<_>

    let publishSet source = new PublishedSet<_>(source)

    let intersect s1 s2 = new VariableSetIntersection<_>(s1, s2) :> IVariableSet<_>
    let union s1 s2 = new VariableSetUnion<_>(s1, s2) :> IVariableSet<_>
    let difference s1 s2 = new VariableSetDifference<_>(s1, s2) :> IVariableSet<_>

    let onSetChange onAdded onRemoved onCleared source =
        new MappedSetAction<_>(source, onAdded, onRemoved, onCleared) :> IVariableSetP<_>

    let filterSet filter source = new FilteredSet<_>(source, filter) :> IVariableSet<_>

    let mapSet level mapping collect source =
        new MappedSet<_,_,_>(source, mapping, collect, byte level) :> IVariableD<_>

    let reduceSet initVal onAdded onRemoved source =
        new ReducedSet<_, _>(source, initVal, onAdded, onRemoved) :> IVariableP<_>
    
